/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef __RDX_BLOCKCOPY_HPP__
#define __RDX_BLOCKCOPY_HPP__

#include <string.h>
#include "rdx_basictypes.hpp"
#include "rdx_pragmas.hpp"

#pragma intrinsic(memcpy)

namespace RDX
{
	namespace Utility
	{
		template<class _T>
		inline void AliasPut(_T *dest, const _T src)
		{
			typedef struct AliasPutStruct { _T v; } RDX_MAY_ALIAS AliasPutStruct_t;
			reinterpret_cast<AliasPutStruct_t *>(dest)->v = src;
		}

		template<class _T>
		inline void ArrayCopy(_T *dest, const _T *src, LargeUInt numUnits)
		{
			while(numUnits--)
				AliasPut(dest++, *src++);
		}

		template<class _T>
		inline void ArrayCopyDown(_T *dest, const _T *src, LargeUInt numUnits)
		{
			dest += numUnits;
			src += numUnits;
			while(numUnits--)
				AliasPut(++dest, *(++src));
		}

		inline bool PtrAligned(const void *ptr, LargeUInt alignment)
		{
			return ((static_cast<const UInt8 *>(ptr) - static_cast<const UInt8 *>(NULL)) % alignment == 0);
		}

		inline void BlockCopyUp(void *dest, const void *src, LargeUInt sz)
		{
			if(sz % sizeof(LargeInt) == 0 && PtrAligned(dest, sizeof(LargeInt)) && PtrAligned(src, sizeof(LargeInt)))
				ArrayCopy<LargeInt>(static_cast<LargeInt *>(dest), static_cast<const LargeInt *>(src), sz / sizeof(LargeInt));
			else
				ArrayCopy<UInt8>(static_cast<UInt8 *>(dest), static_cast<const UInt8 *>(src), sz);
		}

		inline void BlockCopyDown(void *dest, const void *src, LargeUInt sz)
		{
			if(sz % sizeof(LargeInt) == 0 && PtrAligned(dest, sizeof(LargeInt)) && PtrAligned(src, sizeof(LargeInt)))
				ArrayCopyDown<LargeInt>(static_cast<LargeInt *>(dest), static_cast<const LargeInt *>(src), sz / sizeof(LargeInt));
			else
				ArrayCopyDown<UInt8>(static_cast<UInt8 *>(dest), static_cast<const UInt8 *>(src), sz);
		}
		
		inline void BlockMove(void *dest, const void *src, LargeUInt sz)
		{
			if(dest == src)
				return;
			void *destEnd = static_cast<UInt8 *>(dest) + sz;
			const void *srcEnd = static_cast<const UInt8 *>(src) + sz;

			if(dest < src || src >= destEnd || dest >= srcEnd)
				BlockCopyUp(dest, src, sz);
			else
				BlockCopyDown(dest, src, sz);
		}

		inline void BlockCopy(void *dest, const void *src, LargeUInt sz)
		{
			if(sz % sizeof(LargeInt) == 0 && PtrAligned(dest, sizeof(LargeInt)) && PtrAligned(src, sizeof(LargeInt)))
				ArrayCopy<LargeInt>(static_cast<LargeInt *>(dest), static_cast<const LargeInt *>(src), sz / sizeof(LargeInt));
			else
				memcpy(dest, src, sz);
		}

		template<class _T, class _Tblock>
		inline void VolatileUnitCopy(_T *dest, const volatile _T *src)
		{
			LargeUInt count = sizeof(_T) / sizeof(_Tblock);
			_Tblock *bdest = reinterpret_cast<_Tblock *>(dest);
			const volatile _Tblock *bsrc = reinterpret_cast<const volatile _Tblock *>(src);
			while(count--)
				*bdest++ = *bsrc++;
		}

		template<class _T, LargeInt align>
		inline void VolatileCopy(_T *dest, const volatile _T *src)
		{
			if(align % sizeof(LargeInt) == 0 && sizeof(_T) % sizeof(LargeInt) == 0)
				VolatileUnitCopy<_T, LargeInt>(dest, src);
			if(align % sizeof(UInt32) == 0 && sizeof(_T) % sizeof(UInt32) == 0)
				VolatileUnitCopy<_T, UInt32>(dest, src);
			if(align % sizeof(UInt16) == 0 && sizeof(_T) % sizeof(UInt16) == 0)
				VolatileUnitCopy<_T, UInt16>(dest, src);
			VolatileUnitCopy<_T, UInt8>(dest, src);
		}
	}
}

#endif
